package anet

import (
	"sync"
	"sync/atomic"
	"time"

	"gitee.com/AlexWoo/arpc"
	"gitee.com/AlexWoo/arpc/alog"
)

var rtm *timerManager = nil

type timerHandler func(ev *event)

type timer struct {
	m *timerManager

	timerId uint32 // 定时器ID，用于关联和删除使用

	ti      *time.Timer        // 定时器
	expired chan time.Duration // 超时时间，Linux 时间，单位 ms
	stopC   chan bool          // 定时器停止
	handler timerHandler       // 定时器处理函数

	ev *event
}

func (t *timer) loop() {
	for {
		select {
		case <-t.ti.C:
			if t.handler != nil {
				t.handler(t.ev)
				t.ev = nil
			}

			alog.LogDebug(anetLctx, "trigger timer %p [%d] %v", t, t.timerId, t)
			rtm.putTimer(t)
		case e := <-t.expired:
			alog.LogDebug(anetLctx, "reuse timer %p [%d] %v", t, t.timerId, e)
			t.ti = time.NewTimer(e)
		case <-t.stopC:
			alog.LogDebug(anetLctx, "stop timer %p [%d]", t, t.timerId)
			if t.ti.Stop() {
				rtm.putTimer(t)
			} else {
				alog.LogError(anetLctx, "put timer %p [%d] twice", t, t.timerId)
			}
		}
	}
}

func (t *timer) setExpired(e time.Duration) {
	t.expired <- e
}

func (t *timer) stop() {
	t.stopC <- true
}

type timerManager struct {
	timerId uint32 // 当前分配到的定时器 ID

	lock   sync.Mutex
	timers map[uint32]*timer
	tPool  *arpc.Pool
}

func createTimerManager() {
	if rtm == nil {
		rtm = &timerManager{
			timerId: 0,
			timers:  map[uint32]*timer{},
			tPool:   arpc.NewPool(func() any { return &timer{m: rtm} }),
		}
	}
}

func (m *timerManager) getTimer(e time.Duration) *timer {
	t := m.tPool.Get().(*timer)
	t.timerId = atomic.AddUint32(&m.timerId, 1)

	alog.LogDebug(anetLctx, "get timer %p [%d]", t, t.timerId)

	if t.ti == nil {
		t.ti = time.NewTimer(e)
		t.expired = make(chan time.Duration, 1)
		t.stopC = make(chan bool, 1)

		go t.loop()
	} else {
		t.setExpired(e)
	}

	m.lock.Lock()
	defer m.lock.Unlock()

	m.timers[t.timerId] = t

	return t
}

func (m *timerManager) putTimer(t *timer) {
	alog.LogDebug(anetLctx, "put timer %p [%d]", t, t.timerId)

	if t.ev != nil {
		putEvent(t.ev)
		t.ev = nil
	}

	m.lock.Lock()
	defer m.lock.Unlock()

	if t := m.timers[t.timerId]; t != nil {
		delete(m.timers, t.timerId)
		m.tPool.Put(t)
	} else {
		alog.LogError(anetLctx, "put timer %p twice", t)
	}
}

func addTimer(e time.Duration, h timerHandler, ev *event) *timer {
	t := rtm.getTimer(e)

	t.handler = h
	t.ev = ev

	alog.LogDebug(anetLctx, "addTimer %p [%d] %v", t, t.timerId, e)

	return t
}

func delTimer(t *timer) {
	if t == nil {
		return
	}

	alog.LogDebug(anetLctx, "delTimer %p [%d]", t, t.timerId)

	t.stop()
}
